package at.ltasat.train.set;

import java.io.File;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;

import at.ltasat.ArgumentClazzData;
import at.ltasat.Configuration;

public final class TrainingSetConfiguration extends Configuration {
	private static final ArgumentClazzData[] DEFAULT_CLASSES;

	static {
		List<ArgumentClazzData> clazzes = new ArrayList<ArgumentClazzData>();
		for(int threads = 1; threads <= 2 * Runtime.getRuntime().availableProcessors(); threads *= 2){
			clazzes.add(new ArgumentClazzData(String.format("--queue=default --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=inverting --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=random --threads=%d", threads), threads, true));
			clazzes.add(new ArgumentClazzData(String.format("--queue=static-occurences --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:1 --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:3 --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:5 --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:10 --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:15 --threads=%d", threads), threads, false));
			clazzes.add(new ArgumentClazzData(String.format("--queue=dynamic-occurences:25 --threads=%d", threads), threads, false));
		}
		DEFAULT_CLASSES = clazzes.toArray(new ArgumentClazzData[clazzes.size()]);
	}

	private final List<ArgumentClazzData> classes;
	private boolean isDefaultClasses;
	private int variables;
	private int clauses;
	private int iterations;
	private long maxTime;
	private int size;
	private boolean isVerboseStatus, isVerboseConfig;

	{
		classes = new LinkedList<ArgumentClazzData>();
		for (ArgumentClazzData defaultClazz : DEFAULT_CLASSES) {
			classes.add(defaultClazz);
		}
		isDefaultClasses = true;
		variables = 333;
		clauses = 10000;
		iterations = 10;
		maxTime = 1000 * 10;
		size = 100;
		isVerboseConfig = false;
		isVerboseStatus = false;
	}

	public static TrainingSetConfiguration get(String[] args) {
		TrainingSetConfiguration config = new TrainingSetConfiguration();
		for (String arg : args) {
			if (arg.startsWith("-")) {
				String name = arg.substring(0, arg.indexOf('='));
				String value = arg.substring(arg.indexOf('=') + 1);
				if (name.equals("-c") || name.equals("--class")) {
					boolean canCreateMore = false;
					int index = value.length();
					if (value.endsWith(":true")) {
						index -= ":true".length();
						canCreateMore = true;
					} else if (value.endsWith(":false")) {
						index -= ":false".length();
						canCreateMore = false;
					}
					int nProcessors = 0;
					index--;
					if (Character.isDigit(value.charAt(index))) {
						char c = value.charAt(index);
						do {
							int digit = c - '0';
							nProcessors = nProcessors * 10 + digit;
							c = value.charAt(--index);
						} while (Character.isDigit(c));
					} else {
						nProcessors = 1;
					}
					String argument = value.substring(0, index);
					config.addClass(new ArgumentClazzData(argument, nProcessors, canCreateMore));
				} else if (name.equals("-s") || name.equals("--size")) {
					config.setSize(Integer.valueOf(value));
				} else if (name.equals("-i") || name.equals("--iterations")) {
					config.setIterations(Integer.valueOf(value));
				} else if (name.equals("-t") || name.equals("--max-time")) {
					config.setMaxTime(Long.valueOf(value));
				} else if (name.equals("-c") || name.equals("--clauses")) {
					config.setClauses(Integer.valueOf(value));
				} else if (name.equals("-v") || name.equals("--variables")) {
					config.setVariables(Integer.valueOf(value));
				} else if (name.equals("-v") || name.equals("--verbose")) {
					for (String vv : value.split(",")) {
						if (vv.equals("config")) {
							config.setVerboseConfig(true);
						} else if (vv.equals("status")) {
							config.setVerboseStatus(true);
						} else {
							throw new IllegalArgumentException("Unknown verbose value: " + vv);
						}
					}
				} else {
					throw new IllegalArgumentException("Unknown property: " + name);
				}
			} else {
				config.setIn(new File(arg));
			}
		}
		if (config.getInFile() == null) {
			throw new IllegalArgumentException("Must specify a file!");
		}
		if (config.getClazzes().length <= 1) {
			throw new IllegalArgumentException("Must specify at least two classes!");
		}
		return config;
	}

	public ArgumentClazzData[] getClazzes() {
		return classes.toArray(new ArgumentClazzData[classes.size()]);
	}

	public int getVariables() {
		return variables;
	}

	public int getClauses() {
		return clauses;
	}

	public int getIterations() {
		return iterations;
	}

	public long getMaxTime() {
		return maxTime;
	}

	public int getSize() {
		return size;
	}

	public boolean isVerboseConfig() {
		return isVerboseConfig;
	}

	public boolean isVerboseStatus() {
		return isVerboseStatus;
	}

	public void addClass(ArgumentClazzData clazz) {
		assert clazz != null;
		if (isDefaultClasses) {
			classes.clear();
			isDefaultClasses = false;
		}
		classes.add(clazz);
	}

	public void setVariables(int variables) {
		if (variables <= 0) {
			throw new IllegalArgumentException("Number of variables must be greater than zero!");
		}
		this.variables = variables;
	}

	public void setClauses(int clauses) {
		if (clauses <= 0) {
			throw new IllegalArgumentException("Number of clauses must be greater than zero!");
		}
		this.clauses = clauses;
	}

	public void setIterations(int iterations) {
		if (iterations <= 0) {
			throw new IllegalArgumentException("Number of iterations must be greater than zero!");
		}
		this.iterations = iterations;
	}

	public void setMaxTime(long time) {
		if (time <= 0) {
			throw new IllegalArgumentException("Time must be greater than zero!");
		}
		this.maxTime = time;
	}

	public void setSize(int size) {
		if (size < 0) {
			throw new IllegalArgumentException("Validation size must be at least zero!");
		}
		this.size = size;
	}

	public void setVerboseStatus(Boolean value) {
		this.isVerboseStatus = value;
	}

	public void setVerboseConfig(Boolean value) {
		this.isVerboseConfig = value;
	}

	@Override
	public String toString() {
		StringBuilder string = new StringBuilder();
		string.append(String.format("--verbose=%s,%s ", isVerboseConfig ? "config" : "", isVerboseStatus ? "status" : ""));
		for (ArgumentClazzData clazz : classes) {
			string.append(String.format("--class=%s:%d:%s ", clazz.getArgument(), clazz.nProcessors(), String.valueOf(clazz.canCreateMore())));
		}
		string.append(String.format("--size=%d ", size));
		string.append(String.format("--iterations=%d ", iterations));
		string.append(String.format("--max-time=%d ", maxTime));
		string.append(String.format("--clauses=%d ", clauses));
		string.append(String.format("--variables=%d ", variables));
		if (isInFile()) {
			string.append(getInFile().getAbsolutePath());
		}
		return string.toString().trim();
	}
}
